package com.cly.order.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cly.api.clients.CartClient;
import com.cly.api.clients.ItemClient;
import com.cly.common.entity.item.po.Item;
import com.cly.common.entity.order.dto.OrderDTO;
import com.cly.common.entity.order.dto.OrderDetailDto;
import com.cly.common.entity.order.po.Order;
import com.cly.common.entity.order.po.OrderDetail;
import com.cly.common.entity.order.vo.OrderVO;
import com.cly.common.entity.result.Result;
import com.cly.common.exception.CommonException;
import com.cly.common.util.ThreadLocalUtil;
import com.cly.order.constants.MqConstant;
import com.cly.order.mapper.OrderMapper;
import com.cly.order.service.OrderDetailService;
import com.cly.order.service.OrderService;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Service
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private OrderDetailService orderDetailService;
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private CorrelationData correlationData;

    @Autowired
    private ItemClient itemClient;

    @Autowired
    private CartClient cartClient;

    /**
     * 创建订单
     * @param orderDTO
     * @return
     */
    @Override
    @GlobalTransactional
    public Result createOrder(OrderDTO orderDTO) {
        // 1.创建订单对象
        Order order = new Order();
        // 2.获取下单中的商品详情（商品id、购买数量）集合
        List<OrderDetailDto> orderDetails = orderDTO.getOrderDetails();
        // 3.将商品详情转换成Map集合（id为key，数量为value）
        Map<Long, Integer> ItemNumMap = orderDetails.stream().
                collect(Collectors.toMap(OrderDetailDto::getItemId, OrderDetailDto::getNum));
        // 4.将商品id单独转换成集合用于向商品微服务请求商品数据
        List<Long> itemIdList = orderDetails.stream().map(OrderDetailDto::getItemId).collect(Collectors.toList());
//        Set<Long> itemId = ItemNumMap.keySet();   把ItemNumMap的key（itemId）转换成set集合
        // 5.请求商品数据
        List<Item> itemList = itemClient.queryItemsByIds(itemIdList);
        // 6.计算订单总价
        Integer totalFee = 0;
        for (Item item : itemList) {
            totalFee += item.getPrice() * ItemNumMap.get(item.getId());
        }
//        Integer totalFee = itemList.stream().mapToInt(item -> item.getPrice() * ItemNumMap.get(item.getId())).sum();
        order.setTotalFee(totalFee);
        // 7.设置订单信息
        order.setUserId(ThreadLocalUtil.get());
        order.setPayMentType(orderDTO.getPayMentType());
        order.setStatus(1);
        // 8.写入数据库
        this.save(order);
        // 9.编写订单详情
        List<OrderDetail> orderDetailList = createOrderDetail(order.getId(), orderDetails);
        orderDetailService.saveBatch(orderDetailList);

        // 10.调用商品微服务扣减库存
        try {
            itemClient.deductStock(orderDetails);
        } catch (Exception e) {
            // 修改数据库中的订单状态
            this.lambdaUpdate().eq( Order::getId, order.getId()).set( Order::getStatus, 4).update();
            throw new CommonException("库存不足", e);
        }

        // 11.上面操作没问题，说明下单成功，发送 消息 给购物车微服务删除购物车中对应的商品
//        cartClient.deleteCartByIds(itemIdList);
        log.info("下单成功，删除购物车中对应的商品");
        try {
            // 将购物车中要删除的商品id和该用户id通过消息发送给购物车微服务
            Map<String, Object> itemIdListAndUserId = new HashMap<>();
            itemIdListAndUserId.put( "itemIdList", itemIdList);
            itemIdListAndUserId.put( "userId", ThreadLocalUtil.get());
            rabbitTemplate.convertAndSend("cart.clear.queue",
                    "order.create", itemIdListAndUserId, correlationData);
        } catch (AmqpException e) {
            throw new CommonException("删除购物车商品失败",e);
        }

        // 发送延迟消息，过后检查订单是否支付
        Map<String, Object> data = new HashMap<>();
        data.put( "orderId", order.getId());
        data.put("orderDetails", orderDetails);
        rabbitTemplate.convertAndSend(
                MqConstant.DELAY_EXCHANGE_NAME,
                 MqConstant.DELAY_ORDER_KEY,
                data,
                message -> {
                    // TODO  过后把时间改成15分钟，毫秒制
                    message.getMessageProperties().setDelay(1000);
                    return message;
                }
        );

        return Result.success();
    }

    /**
     * 根据订单id查询订单信息
     * @param id
     * @return
     */
    @Override
    public Result getOrderById(Long id) {
        try {
            OrderVO orderVo = orderMapper.getOrderById(id);
            return Result.success(orderVo);
        } catch (Exception e) {
            throw new CommonException("查询订单失败",e);
        }
    }

    /**
     *  修改订单状态
     * @param orderId
     * @param status
     */
    @Override
    public void updateOrderStatus(Long orderId, Integer status) {
        this.lambdaUpdate().eq(Order::getId,orderId).set(Order::getStatus,status).update();
    }

    /**
     * 取消订单,恢复库存
     * @param orderId
     */
    @Override
    public void cancelOrder(Long orderId, List<OrderDetailDto> orderDetails) {
        this.lambdaUpdate().eq(Order::getId,orderId).set( Order::getStatus,4).update();
        // 远程调用商品微服务恢复库存
        itemClient.recoverStock(orderDetails);
    }

    /**
     * 创建订单详情
     * @param id
     * @param orderDetails
     */
    private List<OrderDetail> createOrderDetail(Long id, List<OrderDetailDto> orderDetails) {
         List<OrderDetail> orderDetailList = orderDetails.stream().map(orderDetailDto -> {
             OrderDetail orderDetail = new OrderDetail();
             orderDetail.setOrderId(id);
             orderDetail.setItemId(orderDetailDto.getItemId());
             orderDetail.setNum(orderDetailDto.getNum());
             return orderDetail;
         }).collect(Collectors.toList());
         return orderDetailList;
    }
}
